package org.jeecg.feishu.api.service.impl;

import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.jeecg.feishu.api.config.FeishuConfig;
import org.jeecg.feishu.api.constant.FeishuApiConstants;
import org.jeecg.feishu.api.dto.DeptCondition;
import org.jeecg.feishu.api.dto.DeptPageResponse;
import org.jeecg.feishu.api.dto.DeptSyncDto;
import org.jeecg.feishu.api.dto.DeptV3Response;
import org.jeecg.feishu.api.dto.EmployeeCondition;
import org.jeecg.feishu.api.dto.EmployeePageResponse;
import org.jeecg.feishu.api.dto.EmployeeSyncDto;
import org.jeecg.feishu.api.dto.FeishuRequest;
import org.jeecg.feishu.api.service.FeishuApiService;
import org.jeecg.feishu.api.service.FeishuContractService;
import org.jeecg.feishu.common.config.IStore;
import org.jeecg.feishu.common.exception.FeishuException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import lombok.extern.slf4j.Slf4j;

/**
 * 飞书数据服务
 */
@Service("feishuDataService")
@Slf4j
public class FeishuContractServiceImpl implements FeishuContractService {

    @Autowired
    private FeishuApiService feishuApiService;
    @Autowired
    private FeishuConfig feishuConfig;

    @Override
    public EmployeeSyncDto getEmployee(String employeeId) {
        FeishuRequest<String, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_USER_PATH, "GET", "", new String(), (opt) -> {
            opt.getPathParams().put("user_id", employeeId);
        });
        String response = feishuApiService.send(feishuConfig, request);
        EmployeeSyncDto employeeSyncDto = JSONObject.parseObject(response).getJSONObject("user").toJavaObject(EmployeeSyncDto.class);
        return employeeSyncDto;
    }

    @Override
    public void createEmployee(EmployeeSyncDto employeeSyncDto) throws FeishuException {
        if (CollectionUtils.isEmpty(employeeSyncDto.getDepartmentIds())) {
            employeeSyncDto.setDepartmentIds(Arrays.asList(feishuConfig.getRootDeptId()));
        }
        FeishuRequest<EmployeeSyncDto, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_USERS_PATH, "POST", employeeSyncDto, "", (opt) -> {
            opt.getQueryParams().put("department_id_type", "department_id");
            opt.getQueryParams().put("user_id_type", "user_id");
        });
        feishuApiService.send(feishuConfig, request);
    }

    @Override
    public void updateEmployee(EmployeeSyncDto employeeSyncDto) throws FeishuException {
        if (CollectionUtils.isEmpty(employeeSyncDto.getDepartmentIds())) {
            employeeSyncDto.setDepartmentIds(Arrays.asList(feishuConfig.getRootDeptId()));
        }
        FeishuRequest<EmployeeSyncDto, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_USER_PATH, "PUT", employeeSyncDto, "", (opt) -> {
            opt.getPathParams().put("user_id", employeeSyncDto.getUserId());
            opt.getQueryParams().put("department_id_type", "department_id");
            opt.getQueryParams().put("user_id_type", "user_id");
        });
        feishuApiService.send(feishuConfig, request);
    }

    @Override
    public void employeeLeave(String userId) throws FeishuException {
        FeishuRequest<EmployeeSyncDto, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_USER_PATH, "DELETE", new EmployeeSyncDto(), "", (opt) -> {
            opt.getPathParams().put("user_id", userId);
            opt.getQueryParams().put("department_id_type", "department_id");
            opt.getQueryParams().put("user_id_type", "user_id");
        });
        feishuApiService.send(feishuConfig, request);
    }

    @Override
    public List<EmployeeSyncDto> pageEmployee(EmployeeCondition condition) throws FeishuException {
        if (condition == null || CollectionUtils.isEmpty(condition.getEmployeeIds())) {
            throw new FeishuException(400, "请填写查询参数employeeIds");
        }
        List<String> employeeIds = condition.getEmployeeIds();

        List<EmployeePageResponse.EmployeeInfo> employeeInfos = splitExecute(employeeIds, 100, empIds -> {
            FeishuRequest<String, EmployeePageResponse> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V1_USERS_PATH, "GET", "", new EmployeePageResponse(), opt -> {
                opt.getQueryParams().put("employee_ids", empIds);
            });
            EmployeePageResponse response = feishuApiService.send(feishuConfig, request);
            return response != null && !CollectionUtils.isEmpty(response.getUserInfos()) ? response.getUserInfos() : Lists.newArrayList();
        });

        List<EmployeeSyncDto> resultList = Lists.newArrayList();

        if (CollectionUtils.isEmpty(resultList)) {
            return resultList;
        }

        resultList = employeeInfos.stream().map(info -> {
            EmployeeSyncDto dto = new EmployeeSyncDto();
            dto.setCity(info.getCity());
            dto.setCountry(info.getCountry());
            dto.setDepartmentIds(info.getDepartments());
            dto.setEmail(info.getEmail());
            dto.setEmployeeNo(info.getEmployeeNo());
            dto.setEmployeeType(info.getEmployeeType());
            dto.setGender(info.getGender());
            dto.setJoinTime(info.getJoinTime());
            dto.setLeaderUserId(info.getLeaderEmployeeId());
            dto.setMobile(info.getMobile());
            dto.setName(info.getName());
            dto.setUserId(info.getEmployeeId());
            dto.setWorkStation(info.getWorkStation());
            // 用户状态
            dto.setStatus(new EmployeeSyncDto.UserStatus(info.getStatus()));
            return dto;
        }).collect(Collectors.toList());

        return resultList;
    }

    @Override
    public List<DeptSyncDto> pageDeptV3(DeptCondition condition) throws FeishuException {
        FeishuRequest<String, DeptV3Response> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_DEPARTMENTS_PATH, "GET", "", new DeptV3Response(), opt -> {
            if (StringUtils.isNotEmpty(condition.getDepartmentIdType())) {
                opt.getQueryParams().put("department_id_type", condition.getDepartmentIdType());
            }
            if (StringUtils.isNotEmpty(condition.getParentDepartmentId())) {
                opt.getQueryParams().put("parent_department_id", condition.getParentDepartmentId());
            }
        });
        List<DeptV3Response> response = feishuApiService.pageQuery(feishuConfig, request);

        List<DeptSyncDto> resultList = Lists.newArrayList();

        if (CollectionUtils.isEmpty(response)) {
            return resultList;
        }

        resultList = response.stream().map(info -> {
            DeptSyncDto dto = new DeptSyncDto();
            dto.setName(info.getName());
            dto.setDepartmentId(info.getDepartmentId());
            dto.setLeaderUserId(info.getLeaderUserId());
            dto.setParentDepartmentId(info.getParentDepartmentId());
            dto.setCreateGroupChat(StringUtils.isNotBlank(info.getChatId()));
            DeptSyncDto.DepartmentStatus departmentStatus = new DeptSyncDto.DepartmentStatus();
            departmentStatus.setDeleted(info.getStatus().isDeleted());
            dto.setStatus(departmentStatus);
            dto.setOrder(info.getOrder());
            return dto;
        }).collect(Collectors.toList());

        return resultList;
    }


    @Override
    public List<DeptSyncDto> pageDept(DeptCondition condition) throws FeishuException {
        if (condition == null || CollectionUtils.isEmpty(condition.getDepartmentIds())) {
            throw new FeishuException(400, "请填写查询参数departmentIds");
        }

        List<String> departmentIds = condition.getDepartmentIds();

        List<DeptPageResponse.DeptInfo> deptInfos = splitExecute(departmentIds, 100, deprIds -> {
            FeishuRequest<String, DeptPageResponse> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V1_DEPARTMENTS_PATH, "GET", "", new DeptPageResponse(), opt -> {
                opt.getQueryParams().put("department_ids", deprIds);
            });
            DeptPageResponse response = feishuApiService.send(feishuConfig, request);
            return response != null && !CollectionUtils.isEmpty(response.getDepartmentInfos()) ? response.getDepartmentInfos() : Lists.newArrayList();
        });

        List<DeptSyncDto> resultList = Lists.newArrayList();

        if (CollectionUtils.isEmpty(deptInfos)) {
            return resultList;
        }

        resultList = deptInfos.stream().map(info -> {
            DeptSyncDto dto = new DeptSyncDto();
            dto.setDepartmentId(info.getId());
            dto.setLeaderUserId(info.getLeaderEmployeeId());
            dto.setParentDepartmentId(info.getParentId());
            dto.setCreateGroupChat(StringUtils.isNotBlank(info.getChatId()));
            DeptSyncDto.DepartmentStatus departmentStatus = new DeptSyncDto.DepartmentStatus();
            departmentStatus.setDeleted(info.getStatus() == 0);
            dto.setStatus(departmentStatus);
            return dto;
        }).collect(Collectors.toList());

        return resultList;
    }

    @Override
    public DeptSyncDto getDept(DeptCondition deptCondition) throws FeishuException {
        if (deptCondition == null || StringUtils.isBlank(deptCondition.getDepartmentId())) {
            throw new FeishuException(400, "查询参数错误：" + deptCondition);
        }

        IStore store = feishuConfig.getStore();
        Map<String, DeptSyncDto> cacheMap = (Map<String, DeptSyncDto>) store.get(feishuConfig.getCacheKey(FeishuConfig.DEPT_INFO_KEY));

        if (CollectionUtils.isEmpty(cacheMap)) {
            cacheMap = Maps.newHashMap();
        }

        String departmentId = deptCondition.getDepartmentId();

        if (cacheMap.containsKey(departmentId)) {
            log.info("查询部门:{}, 命中缓存, 返回结果：{}", departmentId, cacheMap.get(departmentId));
            return cacheMap.get(departmentId);
        }

        FeishuRequest<String, DeptSyncDto> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_DEPARTMENT_PATH, "GET", "", new DeptSyncDto(), (opt) -> {
            opt.getPathParams().put("department_id", departmentId);
            opt.getQueryParams().put("department_id_type", "department_id");
        });

        DeptSyncDto response = feishuApiService.send(feishuConfig, request);

        if (Objects.nonNull(response)) {
            cacheMap.put(deptCondition.getDepartmentId(), response);
            store.put(feishuConfig.getCacheKey(FeishuConfig.DEPT_INFO_KEY), cacheMap, 30, TimeUnit.MINUTES);
        }

        return response;
    }

    @Override
    public void createDept(DeptSyncDto deptSyncDto) throws FeishuException {
        if (StringUtils.isBlank(deptSyncDto.getParentDepartmentId())) {
            deptSyncDto.setParentDepartmentId(feishuConfig.getRootDeptId());
        }
        FeishuRequest<DeptSyncDto, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_DEPARTMENTS_PATH, "POST", deptSyncDto, "", (opt) -> {
            opt.getQueryParams().put("department_id_type", "department_id");
            opt.getQueryParams().put("user_id_type", "user_id");
        });
        feishuApiService.send(feishuConfig, request);
    }

    @Override
    public void updateDept(DeptSyncDto deptSyncDto) throws FeishuException {
        if (StringUtils.isBlank(deptSyncDto.getParentDepartmentId())) {
            deptSyncDto.setParentDepartmentId(feishuConfig.getRootDeptId());
        }
        FeishuRequest<DeptSyncDto, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_DEPARTMENT_PATH, "PUT", deptSyncDto, "", (opt) -> {
            opt.getPathParams().put("department_id", deptSyncDto.getDepartmentId());
            opt.getQueryParams().put("department_id_type", "department_id");
            opt.getQueryParams().put("user_id_type", "user_id");
        });
        feishuApiService.send(feishuConfig, request);
    }

    @Override
    public void deleteDept(String deptId) throws FeishuException {
        FeishuRequest<String, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V3_DEPARTMENT_PATH, "DELETE", "", "", (opt) -> {
            opt.getPathParams().put("department_id", deptId);
            opt.getQueryParams().put("department_id_type", "department_id");
            opt.getQueryParams().put("user_id_type", "user_id");
        });
        feishuApiService.send(feishuConfig, request);
    }

    @Override
    public String deptIdMapping(String deptId) {
        IStore store = feishuConfig.getStore();
        Map<String, String> cacheMap = (Map<String, String>) store.get(feishuConfig.getCacheKey(FeishuConfig.DEPT_ID_MAPPING_KEY));

        if (CollectionUtils.isEmpty(cacheMap)) {
            cacheMap = Maps.newHashMap();
        }

        if (StringUtils.isNotBlank(cacheMap.get(deptId))) {
            return cacheMap.get(deptId);
        }

        FeishuRequest<String, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V1_DEPARTMENT_MAPPING_PATH, "GET", "", "", (opt) -> {
            opt.getPathParams().put("department_id", deptId);
        });

        String response = feishuApiService.send(feishuConfig, request);

        if (StringUtils.isNotBlank(response)) {
            cacheMap.put(deptId, response);
            store.put(feishuConfig.getCacheKey(FeishuConfig.DEPT_ID_MAPPING_KEY), cacheMap, 30, TimeUnit.MINUTES);
            return response;
        }

        return null;
    }

    @Override
    public String employeeIdMapping(String employeeId) {
        IStore store = feishuConfig.getStore();
        Map<String, String> cacheMap = (Map<String, String>) store.get(feishuConfig.getCacheKey(FeishuConfig.USER_ID_MAPPING_KEY));

        if (CollectionUtils.isEmpty(cacheMap)) {
            cacheMap = Maps.newHashMap();
        }

        if (StringUtils.isNotBlank(cacheMap.get(employeeId))) {
            return cacheMap.get(employeeId);
        }

        FeishuRequest<String, String> request = FeishuRequest.newRequestByTenant(FeishuApiConstants.CONTACT_V1_USER_MAPPING_PATH, "GET", "", "", (opt) -> {
            opt.getPathParams().put("user_id", employeeId);
        });

        String response = feishuApiService.send(feishuConfig, request);

        if (StringUtils.isNotBlank(response)) {
            cacheMap.put(employeeId, response);
            store.put(feishuConfig.getCacheKey(FeishuConfig.USER_ID_MAPPING_KEY), cacheMap, 30, TimeUnit.MINUTES);
            return response;
        }

        return null;
    }

    /**
     * 分批次执行
     *
     * @param objList
     * @param everyTimeSize
     * @param execute
     * @param <T>
     * @param <R>
     */
    public static <T, R> List<R> splitExecute(List<T> objList, int everyTimeSize, Function<List<T>, List<R>> execute) {
        if (objList == null || objList.size() == 0) {
            return null;
        }

        int totalPage = (objList.size() + everyTimeSize - 1) / everyTimeSize;

        List<R> list = Lists.newArrayList();

        for (int i = 0; i < totalPage; i++) {
            int fromIndex = i * everyTimeSize;
            int toIndex = Math.min((i + 1) * everyTimeSize, objList.size());
            List<R> resultList = execute.apply(objList.subList(fromIndex, toIndex));
            if (CollectionUtils.isEmpty(resultList)) {
                break;
            }
            list.addAll(resultList);
        }

        return list;
    }

    public static void main(String[] args) throws Exception{
        System.out.println(0b00000001);
        System.out.println(0b00000010);
        System.out.println(0b00000100);
        System.out.println((0b00000100 & 4) == 4);
        System.out.println((0b00000100 & 2) == 2);
        System.out.println((0b00000100 ^ 0) == 4);

        Date date = new SimpleDateFormat("yyyy-MM-dd").parse("2005-06-09");
        String now = new SimpleDateFormat("yyyy年MM月dd日").format(date);

        System.out.println(date.getTime());
    }

}
